package session

import (
	"context"
    "github.com/liuxd6825/dapr-go-ddd-sdk/ddd/ddd_repository"
    "github.com/liuxu6825/dapr-go-ddd-example/pkg/cmd-service/infrastructure/logs"
	"github.com/liuxu6825/dapr-go-ddd-example/pkg/query-service/infrastructure/db/dao/mongo_dao"
	"github.com/liuxu6825/dapr-go-ddd-example/pkg/query-service/infrastructure/db/dao/neo4j_dao"
    "github.com/liuxu6825/dapr-go-ddd-example/pkg/query-service/infrastructure/db/dao/mysql_dao"

)

type Func func(ctx context.Context) error
type SessionType int
type doSession func(i int) error

type Options struct {
    mongo *SessionType
	neo4j *SessionType
	mysql *SessionType
}

const (
	NoSession SessionType = iota
	ReadSession
	WriteSession
)


func NewOptions(opts ...*Options) *Options {
	return &Options{}
}

func MergeOptions(opts ...*Options) *Options {
	opt := &Options{}
	w := WriteSession
    opt.mongo = &w
    opt.neo4j = &w
    opt.mysql = &w

	for _, o := range opts {
		if o.mongo != nil {
			opt.mongo = o.mongo
		}
		if o.neo4j != nil {
			opt.neo4j = o.neo4j
		}
        if o.mysql != nil {
            opt.mysql = o.mysql
        }
	}
	return opt
}

func (o *Options) SetMongo(v SessionType) *Options {
    o.mongo = &v
    return o
}

func (o *Options) GetMongo() SessionType {
    if o.mongo == nil {
        return NoSession
    }
    return *o.mongo
}

func (o *Options) SetNeo4j(v SessionType) *Options {
	o.neo4j = &v
	return o
}

func (o *Options) GetNeo4j() SessionType {
	if o.neo4j == nil {
		return NoSession
	}
	return *o.neo4j
}

func (o *Options) SetMySql(v SessionType) *Options {
	o.mysql = &v
	return o
}

func (o *Options) GetMySql() SessionType {
	if o.mysql == nil {
		return NoSession
	}
	return *o.mysql
}

func StartSession(ctx context.Context, fun Func, opts ...*Options) error {
	var start doSession
	var sessions []ddd_repository.Session
	var err error

	opt := MergeOptions(opts...)
	if opt.GetMongo() != NoSession {
		sessions = append(sessions, mongo_dao.NewSession(opt.GetMongo() == WriteSession))
	}
	if opt.GetNeo4j() != NoSession {
		sessions = append(sessions, neo4j_dao.NewSession(opt.GetNeo4j() == WriteSession))
	}
	if opt.GetMySql() != NoSession {
		sessions = append(sessions, mysql_dao.NewSession(opt.GetMySql() == WriteSession))
	}

	start = func(i int) error {
		s := sessions[i]
		return s.UseTransaction(ctx, func(ctx context.Context) error {
			if i < 1{
				return fun(ctx)
			}
			return start(i-1)
		})
	}

	length := len(sessions)
	if length > 0 {
		err = start(length-1)
	} else {
		err = fun(ctx)
	}

	if err != nil {
		logs.Errorln("db.StartSession()", err)
	}
	return err
}
